package model;
/**
 * Puzzle class manages a rectangular puzzle of characters.
 * This is the model that is independent of the user interface.
 * From the user's perspective, rows and columns are numbered
 * starting at 1.
 * @author Beth Katz
 */
public class Puzzle {
	private int theRows;
	private int theCols;
	private PuzzleCell [ ] [ ] theBoard;
	private String whereFound;
	
	
	/**
	 * Builds an empty puzzle.
	 */
	public Puzzle( ) {
		theRows = 0;
		theCols = 0;
		whereFound = null;
		theBoard = null;
	}
	
	/**
	 * Builds a puzzle from the provided matrix.
	 * @param m a matrix of characters
	 */
	public Puzzle(char [ ] [ ] m) {
		initFromMatrix(m);
	}
	
	/**
	 * Initialize an existing puzzle from the provided matrix.
	 * @param m a matrix of characters
	 */
	public void initFromMatrix(char [ ] [ ] m) {
		theRows = m.length;
		theCols = m[0].length;
		whereFound = null;
		theBoard = new PuzzleCell[ theRows+2 ][ theCols+2 ];
		fillBorder( );
		fillPuzzle(m);
	}
	
	/**
	 * Fills the puzzle with the fill character which will act as a border.
	 */
	private void fillBorder( ) {
		for (int i = 0; i <= theRows + 1; i++) {
			for (int j = 0; j <= theCols + 1; j++) {
				theBoard [i][j] = PuzzleCell.sharedBorderCell( );
			}
		}
	}
	
	/**
	 * Fills the puzzle with the characters in matrix.
	 * @param m a matrix of characters
	 */
	private void fillPuzzle(char[ ][ ] m) {
		for (int i = 1; i <= theRows; i++) {
			for (int j = 1; j <= theCols; j++) {
				theBoard [i][j] = new PuzzleCell(m[i-1][j-1]);
			}
		}
	}
	
	/**
	 * Returns the number of rows in the puzzle.
	 * @return number of rows in the puzzle (1..number of rows)
	 */
	public int numberOfRows( ) {
		return theRows;
	}
	
	/**
	 * Returns the number of columns in the puzzle.
	 * @return number of columns in the puzzle (1..number of columns)
	 */
	public int numberOfColumns( ) {
		return theCols;
	}
	
	/**
	 * Returns the character at row, col (numbering starts at 1)
	 * @param row the row index (1..number of rows inclusive)
	 * @param col the column index (1..number of columns inclusive)
	 * @return the character at row,col
	 */
	public char charAt(int row, int col) {
		if ((1 <= row && row <= numberOfRows( ))
				&& (1 <= col && col <= numberOfColumns( ))){
			return theBoard[row][col].getData( );
		} else {
			return '*';
		}
	}

	/**
	 * Returns a String describing where word is found in the puzzle.
	 * If word is found, that information is in the returned value.
	 * If the word is not found, the returned value is empty.
	 * @param word word to be found
	 * @return a string description of the word's location in the puzzle
	 * or an empty string if the word is not in the puzzle
	 */
	public String locationInPuzzle(String word) {
		whereFound = new String( );
		for (int i = 1; i <= theRows; i++) {
			for (int j = 1; j <= theCols; j++) {
				if (theBoard [i][j].getData( ) == word.charAt(0) ) {
					searchAt(word, i, j);
				}
			}
		}
		return whereFound;
	}

	/**
	 * Search for word in the puzzle at location r,c.
	 * @param word the word to search for
	 * @param r the row location
	 * @param c the column location
	 */
	private void searchAt(String word, int r, int c) {
		for (int dR = -1; dR <= 1; dR++) {
			for (int dC = -1; dC <= 1; dC++) {
				if (!((dR == 0) && (dC == 0) ) ) { // not 0,0
					searchThisWay(word, r, c, dR, dC);
				}
			}
		}
	}

	/**
	 * Search for word in the puzzle at location r,c going in dR,dC direction.
	 * @param word the word to search for
	 * @param r the row location
	 * @param c the column location
	 * @param dR the row direction to search in
	 * @param dC the column direction to search in
	 */
	private void searchThisWay(String word, int r, int c, int dR, int dC) {
		int w = 0; // index into word
		int row = r;
		int col = c;
		final char NULL = '\0';
		String originalWord = word;
		
		word = word + NULL;
		
		while(word.charAt(w) == theBoard[row][col].getData( )) {
			row += dR;
			col += dC;
			w++;
		}
		if (word.charAt(w) == NULL) {
			whereFound += originalWord + " at " + r + "," + c 
		    		+ " going " + dir(dR, dC) + '\n';
			markUsed(originalWord.length( ), r, c, dR, dC);
 		}
	}
	/**
	 * Mark used letters of word in the puzzle at location r,c going in dR,dC direction.
	 * @param number of letters to mark
	 * @param r the starting row location
	 * @param c the starting column location
	 * @param dR the row direction to move in
	 * @param dC the column direction to move in
	 */
	private void markUsed(int length, int r, int c, int dR, int dC) {

		while(length > 0) {
			theBoard[r][c].setUsed(true);
			r += dR;
			c += dC;
			length--;
		}
	}

	/**
	 * reset all cells to unused
	 */
	public void resetUsed( ) {
		for (int i = 1; i <= theRows; i++) {
			for (int j = 1; j <= theCols; j++) {
				theBoard [i][j].setUsed(false);
			}
		}
	}

	/**
	 * @param r the row location
	 * @param c the column location
	 * @return whether or not letter at location r,c is used
	 */
	public boolean charUsedAt(int r, int c) {
		if ((1 <= r && r <= numberOfRows( ))
				&& (1 <= c && c <= numberOfColumns( ))){
			return theBoard[r][c].isUsed( );
		} else {
			return false;
		}
	}

	/**
	 * @return string of unused letters in puzzle
	 */
	public String stringOfRemainingLetters( ) {
		String answer = new String( );

		for (int i = 1; i <= theRows; i++) {
			for (int j = 1; j <= theCols; j++) {
				if (!theBoard [i][j].isUsed( )) {
					answer += theBoard [i][j].getData( );
				}
			}
		}
		return answer;
	}
	
	/**
	 * Returns a string representation of the dR,dC direction.
	 * @param dR the row direction (-1 is north and 1 is south)
	 * @param dC the column direction (-1 is west and 1 is east)
	 * @return string description corresponding to the parameters
	 */
	private String dir(int dR, int dC) {
		String s = "";
		
		if (dR == -1) {
			s += 'N';
		} else if (dR == 1) {
			s += 'S';			
		}
		if (dC == -1) {
			s += 'W';
		} else if (dC == 1) {
			s += 'E';			
		}
		return s;
	}
	
	/**
	 * count the number of times a character appears 
	 * in the puzzle
	 * @param ch the character to search for
	 * @return the number of times ch appears in puzzle
	 */
	public int numMatches(char ch) {
		int count = 0;
		
		for (int i = 1; i <= theRows; i++) {
			for (int j = 1; j <= theCols; j++) {
				if (theBoard [i][j].getData( ) == ch) {
					count++;
				}
			}
		}
		return count;	
	}

}
